package com.zhj.edu.messagecenterserver.service.impl;

import com.alibaba.druid.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zhj.edu.constance.DeviceConstance;
import com.zhj.edu.exception.handler.BusinessException;
import com.zhj.edu.messagecenterserver.mapper.DeviceMapper;
import com.zhj.edu.messagecenterserver.mapper.DeviceTopicMapper;
import com.zhj.edu.messagecenterserver.mapper.MqttUserMapper;
import com.zhj.edu.messagecenterserver.mapper.ProductMapper;
import com.zhj.edu.messagecenterserver.service.DeviceService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhj.edu.pojo.Device;
import com.zhj.edu.pojo.DeviceTopic;
import com.zhj.edu.pojo.MqttUser;
import com.zhj.edu.pojo.Product;
import com.zhj.edu.result.ResponseCode;
import com.zhj.edu.utils.MD5;
import com.zhj.edu.vo.DriverMqttAuthVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * <p>
 * 设备表 服务实现类
 * </p>
 *
 * @author secret
 * @since 2024-10-09
 */
@Service
@Slf4j
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements DeviceService , BeanFactoryAware {

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private MqttUserMapper mqttUserMapper;

    @Autowired
    private DeviceTopicMapper deviceTopicMapper;

    //拿到这个工厂的目的是为了去IOC容器中找到对象的
    private BeanFactory beanFactory;

    @Value("${driver.mqtt.qos}")
    private Integer qos=0;

    @Value("${driver.mqtt.url}")
    private String url;

    @Override
    public DriverMqttAuthVo auth(DriverMqttAuthVo driverMqttAuthVo) {
        //第一步：校验参数的合法性
        if (null == driverMqttAuthVo || StringUtils.isEmpty(driverMqttAuthVo.getProductKey()) || StringUtils.isEmpty(driverMqttAuthVo.getDeviceKey()) || StringUtils.isEmpty(driverMqttAuthVo.getDeviceSecret())) {
            throw new BusinessException(ResponseCode.PARAMETER_EXCEPTION);
        }
        //检验产品是否存在
        QueryWrapper<Product> productQueryWrapper = new QueryWrapper<>();
        productQueryWrapper.eq("product_key",driverMqttAuthVo.getProductKey());
        Product product = productMapper.selectOne(productQueryWrapper);
        if (null==product){
            //说明产品不存在
            throw new BusinessException(ResponseCode.PRODUCT_NOT_FOUND_EXCEPTION);
        }
        //执行到这里说明产品存在,就校验设备信息
        QueryWrapper<Device> deviceQueryWrapper = new QueryWrapper<>();
        deviceQueryWrapper.eq("product_id",product.getId());
        deviceQueryWrapper.eq("device_key",driverMqttAuthVo.getDeviceKey());
        deviceQueryWrapper.eq("product_secret",driverMqttAuthVo.getDeviceSecret());
        //查询数据
        Device device = deviceMapper.selectOne(deviceQueryWrapper);
        if (null==device){
            throw new BusinessException(ResponseCode.DEVICE_NOT_FOUND_EXCEPTION);
        }
        //执行到这里说明硬件传递的消息都是正常的...
        //设置设备的状态为激活状态
        device.setActive(2);
        //更新设备信息
        deviceMapper.updateById(device);
        //添加用户名和密码到qf_mqtt_user表中去
        addUsernameAndPasswordToMqttUser(product.getTenantId());
        //我们的应用程序订阅特定的主题
        addSubScribe(device.getDeviceKey());
        //封装返回数据给硬件 方便硬件去认证
        //最后一步:就是封装这个消息 返回给这个硬件 让他去连接服务器 订阅相关主题
        // tcp://10.7.182.56:1883
        url = url.replaceAll("//", "");

        // tcp:10.7.182.56:1883
        String[] split = url.split(":");
        //说白了就是告诉这个硬件EMQX服务器的地址是多少
        driverMqttAuthVo.setUrl(url);
        //设置ip地址
        driverMqttAuthVo.setIp(split[1]);
        //设置端口
        driverMqttAuthVo.setPort(Integer.parseInt(split[2]));

        //下面设置的事这个用户名和密码 这个用户名和密码是告诉硬件认证的账号信息
        driverMqttAuthVo.setUsername(product.getTenantId());
        driverMqttAuthVo.setPassword(product.getTenantId());
        driverMqttAuthVo.setTenantId(product.getTenantId());
        return driverMqttAuthVo;
    }

    /**
     * 订阅主题
     * @param deviceKey
     */
    private void addSubScribe(String deviceKey) {
        //这里就是应用程序去EMQX服务上订阅主题了
        //通过设备key查询设备需要订阅的这个主题是啥
        QueryWrapper<DeviceTopic> deviceTopicQueryWrapper = new QueryWrapper<>();
        deviceTopicQueryWrapper.eq("device_key",deviceKey);
        List<DeviceTopic> deviceTopicList = deviceTopicMapper.selectList(deviceTopicQueryWrapper);
        if (null==deviceTopicList || deviceTopicList.size()==0 || deviceTopicList.size()!=2){
            //说明设备key有误
            throw new BusinessException(ResponseCode.DEVICE_KEY_EXCEPTION);
        }
        //到这里首先要获取这个emqx中配置的out对象
        //下面设计到这个MQTT的配置咋们还没有配置好
        Object mqttInbound = beanFactory.getBean("mqttInbound");
        //这个对象还需要强制类型转换成我们操作EMQX服务器的对象
        MqttPahoMessageDrivenChannelAdapter messageDrivenChannelAdapter= (MqttPahoMessageDrivenChannelAdapter) mqttInbound;
        //执行到这里说明查询到数据了
        for (int i = 0; i < deviceTopicList.size(); i++) {
            //取出设备主题的数据
            DeviceTopic deviceTopic = deviceTopicList.get(i);
            //这里就涉及到Springboot整合EMQX的问题
            //我们这里要从 IOC容器中去拿取这个通信的对象  ?这里就涉及到一个问题了 如何通过代码去IOC容器中取数据呢?
            try {
                messageDrivenChannelAdapter.addTopic(deviceTopic.getTopic(),0);
            }catch (Exception err){
                log.info("{}-当前主题已经订阅过----",deviceTopic.getTopic());
            }
        }
    }

    /**
     * 添加用户名和密码到mqtt_user表中去
     * 这个表的主要作用是用于这个硬件到EMQX服务器去认证使用的
     * 不存在就插入数据，存在就不管
     * @param tenantId
     */
    public void addUsernameAndPasswordToMqttUser(String tenantId) {
        //首先通过租户id查信息
        QueryWrapper<MqttUser> mqttUserQueryWrapper = new QueryWrapper<>();
        mqttUserQueryWrapper.eq("tenant_id",tenantId);
        //查询这个数据
        MqttUser mqttUser = mqttUserMapper.selectOne(mqttUserQueryWrapper);
        if (null==mqttUser){
            //说明这个数据不存在，那么就需要向数据库添加数据
            MqttUser mqttUser1 = new MqttUser();
            mqttUser1.setUsername(tenantId);
            mqttUser1.setPassword(MD5.encrypt(tenantId));
            mqttUser1.setTenantId(tenantId);
            mqttUser1.setIsSuperuser(0);
            mqttUserMapper.insert(mqttUser1);
        }
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory=beanFactory;
    }
}
